package com.michaelbulava.TFS.tasks.vc;

import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.module.ModuleUtil;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vcs.ProjectLevelVcsManager;
import com.intellij.openapi.vcs.VcsDirectoryMapping;
import com.intellij.openapi.vcs.changes.VcsDirtyScope;
import com.intellij.openapi.vcs.changes.VcsDirtyScopeManager;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.vcsUtil.VcsUtil;
import com.michaelbulava.TFS.Messages;
import com.michaelbulava.TFS.TFSVcsModuleComponent;
import com.michaelbulava.TFS.configuration.ProjectPreferences;
import com.michaelbulava.TFS.log.FileLogger;
import com.michaelbulava.TFS.tasks.BackgroundableTask;
import com.michaelbulava.TFS.tpignore.TPIgnoreDocument;
import com.michaelbulava.TFS.vcs.TFSVcs;
import com.michaelbulava.TFS.vcs.utils.TFSFileUtil;
import com.microsoft.tfs.core.TFSTeamProjectCollection;
import com.microsoft.tfs.core.clients.commonstructure.ProjectInfo;
import com.microsoft.tfs.core.clients.versioncontrol.GetOptions;
import com.microsoft.tfs.core.clients.versioncontrol.PendChangesOptions;
import com.microsoft.tfs.core.clients.versioncontrol.path.LocalPath;
import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.*;
import com.microsoft.tfs.core.util.FileEncoding;
import com.microsoft.tfs.util.Check;

import java.net.URI;
import java.text.MessageFormat;
import java.util.*;

/**
 * Created by MichaelA on 7/29/2015.
 */
public class PerformShareTask extends BackgroundableTask {
    private static final Logger log = FileLogger.getInstance(PerformShareTask.class);
    private final ProjectInfo[] selectedProjects;
    private final Workspace workspace;
    private final String projectTFSDestination;
    private final Map<Module, String> moduleMappings;
    private final boolean isEntireProjectShare;

    private final boolean runInBackground;

    public PerformShareTask(Project project, ProjectInfo[] selectedProjects, Workspace workspace,
                            String tfsDestination){
        this(project, selectedProjects, workspace, tfsDestination, false);
    }

    public PerformShareTask(Project project, ProjectInfo[] selectedProjects, Workspace workspace,
                            Map<Module, String> moduleMapping){
        this(project, selectedProjects, workspace, moduleMapping, false);
    }

    public PerformShareTask(Project project, ProjectInfo[] selectedProjects, Workspace workspace,
                            String tfsDestination, boolean backgroundTask){
        super(project, Messages.getString("PerformShareTask.ProjectTitle"), false);
        Check.notNull(project, "project");
        Check.notNull(workspace, "workspace");
        Check.notNullOrEmpty(tfsDestination, "tfsDestination");

        this.selectedProjects = selectedProjects;
        this.workspace = workspace;
        this.projectTFSDestination = tfsDestination;
        this.moduleMappings = new HashMap<Module, String>();
        this.runInBackground = backgroundTask;
        this.isEntireProjectShare = true;
    }


    public PerformShareTask(Project project, ProjectInfo[] selectedProjects, Workspace workspace,
                            Map<Module, String> moduleMapping, boolean backgroundTask){
        super(project, Messages.getString("PerformShareTask.ModuleTitle"), false);
        Check.notNull(project, "project");
        Check.notNull(workspace, "workspace");
        Check.notNull(moduleMapping, "moduleMapping");
        Check.isTrue(!moduleMapping.isEmpty(), "moduleMapping must contain 1 or more item");

//        while (moduleMapping.keys().hasMoreElements()){
//            Module module = moduleMapping.keys().nextElement();
//            String path = moduleMapping.get(module);
//            if (path == null || path.length() == 0)
//                throw new IllegalArgumentException("path for module [" + module.getName() +"] is empty.");
//        }

        this.selectedProjects = selectedProjects;
        this.workspace = workspace;
        this.projectTFSDestination = null;
        this.moduleMappings = moduleMapping;
        runInBackground = backgroundTask;
        isEntireProjectShare = false;
    }

    @Override
    protected boolean runInBackground() {
        return runInBackground;
    }

    @Override
    public void run(ProgressIndicator progressIndicator) {
        try{
            createWorkspaceMapping(progressIndicator);
            createDefaultIgnoreFile(progressIndicator);
            performTFSAdds(progressIndicator);
            associateProjects(progressIndicator);
        } catch(Throwable tr){
            log.error("Failure during share operation", tr);
            Messages.showErrorDialog(myProject, "Error occurred sharing project to TFS: " + tr.getLocalizedMessage(),
                    "Share Failure");
        }
    }

    private void createWorkspaceMapping(ProgressIndicator indicator){
        if (isEntireProjectShare){
            indicator.setText2(Messages.getString("PerformShareTask.Project.WorkspaceMapping"));
            WorkingFolder workingFolder = new WorkingFolder(projectTFSDestination,
                    LocalPath.canonicalize(myProject.getBasePath()), WorkingFolderType.MAP, RecursionType.FULL);
            workspace.createWorkingFolder(workingFolder);
        } else {
            indicator.setText2(Messages.getString("PerformShareTask.Modules.WorkspaceMapping"));
            List<WorkingFolder> workingFolderList = new ArrayList<WorkingFolder>();
            for(Map.Entry<Module, String> item : moduleMappings.entrySet()){
                WorkingFolder workingFolder = new WorkingFolder(item.getValue(),
                        LocalPath.canonicalize(item.getKey().getModuleFile().getParent().getPath()),
                        WorkingFolderType.MAP, RecursionType.FULL);
                workingFolderList.add(workingFolder);
            }
        }
    }

    private void createDefaultIgnoreFile(ProgressIndicator indicator){
        if (isEntireProjectShare){
            Module[] modules = ModuleManager.getInstance(myProject).getModules();
            for(int i = 0; i < modules.length; i++){
                indicator.setText2(MessageFormat.format(Messages.getString("PerformShareTask.CreateIgnoreFileFormat"),
                        new Object[] { modules[i].getName() }));
                TPIgnoreDocument.createDefaultTPIgnoreDocument(modules[i]);
            }
        } else {
            for(Map.Entry<Module, String> moduleItem : moduleMappings.entrySet()){
                indicator.setText2(MessageFormat.format(Messages.getString("PerformShareTask.CreateIgnoreFileFormat"),
                        new Object[] { moduleItem.getKey().getName() }));
                TPIgnoreDocument.createDefaultTPIgnoreDocument(moduleItem.getKey());
            }
        }
    }

    private void performTFSAdds(ProgressIndicator indicator){
        List<VirtualFile> filesToAdd;
        ProjectLevelVcsManager manager = ProjectLevelVcsManager.getInstance(myProject);

        if (isEntireProjectShare){
            indicator.setText2(Messages.getString("PerformShareTask.AssociateProjectToVcs"));
            VfsUtil.markDirtyAndRefresh(false, true, false, myProject.getBaseDir());

            manager.setDirectoryMappings(VcsUtil.addMapping(manager.getDirectoryMappings(), myProject.getBasePath(),
                    TFSVcs.VCS_NAME));
            manager.updateActiveVcss();
            VcsDirtyScopeManager.getInstance(myProject).dirDirtyRecursively(myProject.getBaseDir());

            indicator.setText2(Messages.getString("PerformShareTask.GetFilesToAdd"));
            filesToAdd = TFSFileUtil.recursivelyGetFilesAndFolders(myProject, myProject.getBaseDir());
        } else {
            filesToAdd = new ArrayList<VirtualFile>();
            for(Map.Entry<Module, String> item : moduleMappings.entrySet()){
                indicator.setText2(MessageFormat.format(Messages.getString("PerformShareTask.AssociateModuleToVcsFORMAT"),
                        new Object[]{item.getKey().getName()}));
                VirtualFile moduleFolder = item.getKey().getModuleFile().getParent();
                VfsUtil.markDirtyAndRefresh(false, true, false, moduleFolder);
                manager.setDirectoryMappings(VcsUtil.addMapping(manager.getDirectoryMappings(), moduleFolder.getPath(),
                        TFSVcs.VCS_NAME));

                manager.updateActiveVcss();
                VcsDirtyScopeManager.getInstance(myProject).dirDirtyRecursively(moduleFolder);

                indicator.setText2(Messages.getString("PerformShareTask.GetFilesToAdd"));
                filesToAdd.addAll(TFSFileUtil.recursivelyGetFilesAndFolders(myProject, moduleFolder));
            }
        }
        indicator.setText2("");
        String[] addFileStrings = new String[filesToAdd.size()];
        for(int i = 0; i < filesToAdd.size(); i ++){
            addFileStrings[i] = filesToAdd.get(i).getPath();
        }
        indicator.setText2(Messages.getString("PerformShareTask.PendAdd"));
        workspace.pendAdd(addFileStrings, false, FileEncoding.AUTOMATICALLY_DETECT, LockLevel.UNCHANGED,
                GetOptions.NONE, PendChangesOptions.NONE);

    }

    private void associateProjects(ProgressIndicator indicator){
        ProjectLevelVcsManager manager = ProjectLevelVcsManager.getInstance(myProject);
        if (isEntireProjectShare){
            URI serverURI = workspace.getServerURI();
            Module[]  modules = ModuleManager.getInstance(myProject).getModules();
            for(int i = 0; i < modules.length; i++){
                TFSVcsModuleComponent component = TFSVcsModuleComponent.getInstance(modules[i]);
                //component.setTFSUri(serverURI);
                component.setOffline(false);
            }
            manager.setDirectoryMappings(VcsUtil.addMapping(manager.getDirectoryMappings(), myProject.getBasePath(),
                    TFSVcs.VCS_NAME));

            ProjectPreferences prefs = ProjectPreferences.getProjectInstance(myProject);
            prefs.setProjectServerURI(serverURI);
            prefs.setProjectIsOffline(false);
        } else {
            for(Map.Entry<Module, String> item : moduleMappings.entrySet()){
                TFSVcsModuleComponent component = TFSVcsModuleComponent.getInstance(item.getKey());
                //component.setTFSUri(workspace.getClient().getConnection().getBaseURI());
                component.setOffline(false);
                manager.setDirectoryMappings(VcsUtil.addMapping(manager.getDirectoryMappings(), item.getValue(),
                        TFSVcs.VCS_NAME));
            }
        }
        manager.updateActiveVcss();
//        final String path = mapRoot.equals(projectBaseDir) ? "" : mapRoot.getPath();
//        ProjectLevelVcsManager manager = ProjectLevelVcsManager.getInstance(project);
//        manager.setDirectoryMappings(VcsUtil.addMapping(manager.getDirectoryMappings(), path, CvsVcs2.getInstance(project).getName()));
//        manager.updateActiveVcss();

    }




}
